#include <GL/freeglut.h>
#include <GL/glu.h>
#include <AL/al.h>
#include <stdlib.h>
#include <string.h>
#include <juanmabc.h>
#include "tcGame.h"
#include "tcTrack.h"
#include "tcCheckPoints.h"
#include "tcItems.h"
#include "tcToy.h"
#include "tcStateMain.h"
#include "tcStateRace.h"


#define CONTACTS 512
//static float ambient[4] = { 0.5f, 0.5f, 0.5f, 1.0f }, diffuse[4] = { 1.0f, 1.0f, 1.0f, 1.0f }, specular[4] = { 1.0f, 1.0f, 1.0f, 1.0f }, position[4] = { 0.0f, 100.0f, 0.0f, 1.0f };

static
void
tcToysPrepare(unsigned int chp)
{
	TCGame *game;
	float p[3], *t, f[3], r[16];
	unsigned int i;

	game = tcGame();

	mlPointCopy(tcCheckPointsVertex(game->race.chps, chp % tcCheckPointsVertices(game->race.chps)), game->race.camera);
	t = tcCheckPointsVertex(game->race.chps, (chp + 1) % tcCheckPointsVertices(game->race.chps));
	mlVectorSubTo(t, game->race.camera, f);
	mlVectorNormal(f);
	mlMatrixLoadRotation(r, f, mlVectorY);
	for(i = 0; i < game->options.race.ntoys; i++)
	{
		if(!tcToyIsEnabled(game->race.toys[i]))
			tcToyEnable(game->race.toys[i]);
		game->race.tchps[i] = chp;
		game->race.tgo[i] = 0;
		mlPointLoad(p, (i & 1) ? 5.0f : -5.0f, 15.0f, (float) (i / 2) * 10.0f);
		mlVectorMultMatrix(p, r);
		mlPointAdd(p, game->race.camera);
		tcToyPlace(game->race.toys[i], p, r);
		tcToyDrive(game->race.toys[i], 0, 0, 0, 0);
	}
}

void
tcStateRaceIn(void)
{
	TCGame *game;
	unsigned int strings[5];
	unsigned int i, j;

	game = tcGame();

	mlGenFrustums(1, &game->race.frustum);

	game->race.world = dWorldCreate();
	game->race.space = dHashSpaceCreate(0);
	game->race.joints = dJointGroupCreate(0);
	dWorldSetGravity(game->race.world, 0.0f, -9.8f, 0.0f);

	tcGenTracks(1, &game->race.track);
	tcTrackLoad(game->race.track, game->general.tracks[game->options.race.track]);

	tlGenStrings(5, strings);

	tcGenCheckPointses(1, &game->race.chps);
	tlStringLoad(strings[0], PACKAGE_DATADIR);
	tlStringLoad(strings[1], tlStringData(game->general.tracks[game->options.race.track]));
	tlStringConcatPath(strings[2], 2, strings);
	tlStringLoad(strings[3], L".cchps");
	tlStringConcat(strings[4], 2, &strings[2]);
	tcCheckPointsLoad(game->race.chps, strings[4]);
	tlStringsUnload(5, strings);

	tcGenItemses(1, &game->race.items);
	tlStringLoad(strings[0], PACKAGE_DATADIR);
	tlStringLoad(strings[1], tlStringData(game->general.tracks[game->options.race.track]));
	tlStringConcatPath(strings[2], 2, strings);
	tlStringLoad(strings[3], L".citems");
	tlStringConcat(strings[4], 2, &strings[2]);
	tcItemsLoad(game->race.items, strings[4]);
	tlStringsUnload(5, strings);

	for(i = 0; i < tcItemsVertices(game->race.items); i++)
	{
		float *pi, *pa, *pb, da[3], db[3];

		pi = tcItemsVertex(game->race.items, i);

		game->race.ichps[i] = 0;

		for(j = 0; j < tcCheckPointsVertices(game->race.chps); j++)
		{
			pa = tcCheckPointsVertex(game->race.chps, game->race.ichps[i]);
			pb = tcCheckPointsVertex(game->race.chps, j);
			mlVectorSubTo(pi, pa, da);
			mlVectorSubTo(pi, pb, db);
			if(mlVectorNorm(db) < mlVectorNorm(da))
				game->race.ichps[i] = j;
		}
	}

	tlStringLoad(strings[0], PACKAGE_DATADIR);
	tlStringLoad(strings[1], L"Item.cmesh");
	tlStringConcatPath(strings[2], 2, strings);
	mlGenMeshes(1, &game->race.item);	
	mlMeshLoad(game->race.item, strings[2]);
	mlMeshGenMaterials(game->race.item, strings[0]);
	tlStringsUnload(2, &strings[1]);

	tlStringLoad(strings[1], L"Hammer.cmesh");
	tlStringConcatPath(strings[2], 2, strings);
	mlGenMeshes(1, &game->race.hammer);	
	mlMeshLoad(game->race.hammer, strings[2]);
	mlMeshGenMaterials(game->race.hammer, strings[0]);
	tlStringsUnload(2, &strings[1]);

	tlStringLoad(strings[1], L"Canon.cmesh");
	tlStringConcatPath(strings[2], 2, strings);
	mlGenMeshes(1, &game->race.canon);	
	mlMeshLoad(game->race.canon, strings[2]);
	mlMeshGenMaterials(game->race.canon, strings[0]);
	tlStringsUnload(2, &strings[1]);

	tlStringLoad(strings[1], L"Ball.cmesh");
	tlStringConcatPath(strings[2], 2, strings);
	mlGenMeshes(1, &game->race.ball);	
	mlMeshLoad(game->race.ball, strings[2]);
	mlMeshGenMaterials(game->race.ball, strings[0]);
	tlStringsUnload(2, &strings[1]);

	tlStringLoad(strings[1], L"Mine.cmesh");
	tlStringConcatPath(strings[2], 2, strings);
	mlGenMeshes(1, &game->race.mine);	
	mlMeshLoad(game->race.mine, strings[2]);
	mlMeshGenMaterials(game->race.mine, strings[0]);
	tlStringsUnload(2, &strings[1]);

	mlPointCopy(tcCheckPointsVertex(game->race.chps, 0), game->race.camera);

	memset(game->race.tpads, 0, 8 * 5 * sizeof(unsigned int));
	memset(game->race.tchps, 0, 8 * sizeof(unsigned int));
	memset(game->race.tdistances, 0, 8 * sizeof(float));
	memset(game->race.tpoints, 0, 8 * sizeof(unsigned int));
	memset(game->race.tgo, 0, 8 * sizeof(unsigned int));

	for(i = 0; i < tcItemsVertices(game->race.items); i++)
	{
		game->race.itoys[i] = -1;
		game->race.ibodies[i] = NULL;
	}


	alGenBuffers(game->general.nsfxs, game->race.bsfxs);
	for(i = 0; i < game->general.nsfxs; i++)
	{
		unsigned int sound;

		slGenSounds(1, &sound);
		tlStringLoad(strings[1], tlStringData(game->general.sfxs[i]));
		tlStringConcatPath(strings[2], 2, strings);
		slSoundLoadVORBIS(sound, strings[2]);
		tlStringsUnload(2, &strings[1]);
		slSoundBufferData(sound, game->race.bsfxs[i]);
		slSoundUnload(sound);
		slDeleteSounds(1, &sound);
		alGenSources(game->options.race.ntoys, game->race.tsfxs[i]);
		for(j = 0; j < game->options.race.ntoys; j++)
		{
			alSourcei(game->race.tsfxs[i][j], AL_BUFFER, game->race.bsfxs[i]);
			alSourcef(game->race.tsfxs[i][j], AL_ROLLOFF_FACTOR, 0.1f);
		}
	}

	tcGenToys(game->options.race.ntoys, game->race.toys);
	for(i = 0; i < game->options.race.ntoys; i++)
	{
		tcToyLoad(game->race.toys[i], game->general.toys[game->options.race.toy], game->general.tcolors[i], mlPointZero);
		tcToyDisable(game->race.toys[i]);
		game->race.titems[i] = -1;
		
	}
	tcToysPrepare(0);

	game->race.state = TC_RACE_STATE_FIGHT_POINT;
	game->race.start = 60;

	tlStringUnload(strings[0]);
	tlDeleteStrings(5, strings);

	tlGenStrings(1, &game->race.string);

//	glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
//	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
//	glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
//	glLightfv(GL_LIGHT0, GL_POSITION, position);
//	glEnable(GL_LIGHT0);
//	glEnable(GL_LIGHTING);
}

void
tcStateRaceOut(void)
{
	TCGame *game;
	unsigned int i;

	game = tcGame();

	tlDeleteStrings(1, &game->race.string);

	tcToysUnload(game->options.race.ntoys, game->race.toys);
	tcDeleteToys(game->options.race.ntoys, game->race.toys);

	for(i = 0; i < game->general.nsfxs; i++)
		alDeleteSources(game->options.race.ntoys, game->race.tsfxs[i]);
	alDeleteBuffers(game->general.nsfxs, game->race.bsfxs);

	mlMeshDeleteMaterials(game->race.mine);
	mlMeshUnload(game->race.mine);	
	mlDeleteMeshes(1, &game->race.mine);	

	mlMeshDeleteMaterials(game->race.ball);
	mlMeshUnload(game->race.ball);	
	mlDeleteMeshes(1, &game->race.ball);	

	mlMeshDeleteMaterials(game->race.canon);
	mlMeshUnload(game->race.canon);	
	mlDeleteMeshes(1, &game->race.canon);	

	mlMeshDeleteMaterials(game->race.hammer);
	mlMeshUnload(game->race.hammer);	
	mlDeleteMeshes(1, &game->race.hammer);	

	mlMeshDeleteMaterials(game->race.item);
	mlMeshUnload(game->race.item);	
	mlDeleteMeshes(1, &game->race.item);	

	tcItemsUnload(game->race.items);
	tcDeleteItemses(1, &game->race.items);

	tcCheckPointsUnload(game->race.chps);
	tcDeleteCheckPointses(1, &game->race.chps);

	tcTrackUnload(game->race.track);
	tcDeleteTracks(1, &game->race.track);

	dJointGroupDestroy(game->race.joints);
	dSpaceDestroy(game->race.space);
	dWorldDestroy(game->race.world);

	mlDeleteFrustums(1, &game->race.frustum);
}

void
tcStateRaceKeyboard(unsigned char key, int x, int y)
{
	TCGame *game;

	game = tcGame();

	switch(key)
	{
		case '\r': game->race.tpads[0][4] = 1; break;
		case 's': game->race.tpads[1][0] = 1; break;
		case 'x': game->race.tpads[1][1] = 1; break;
		case 'z': game->race.tpads[1][2] = 1; break;
		case 'c': game->race.tpads[1][3] = 1; break;
		case 'f': game->race.tpads[1][4] = 1; break;
		case 'u': game->race.tpads[2][0] = 1; break;
		case 'j': game->race.tpads[2][1] = 1; break;
		case 'h': game->race.tpads[2][2] = 1; break;
		case 'k': game->race.tpads[2][3] = 1; break;
		case 'p': game->race.tpads[2][4] = 1; break;
		default: break;
	}
}

void
tcStateRaceKeyboardUp(unsigned char key, int x, int y)
{
	TCGame *game;

	game = tcGame();

	switch(key)
	{
		case '\r': game->race.tpads[0][4] = 0; break;
		case 's': game->race.tpads[1][0] = 0; break;
		case 'x': game->race.tpads[1][1] = 0; break;
		case 'z': game->race.tpads[1][2] = 0; break;
		case 'c': game->race.tpads[1][3] = 0; break;
		case 'f': game->race.tpads[1][4] = 0; break;
		case 'u': game->race.tpads[2][0] = 0; break;
		case 'j': game->race.tpads[2][1] = 0; break;
		case 'h': game->race.tpads[2][2] = 0; break;
		case 'k': game->race.tpads[2][3] = 0; break;
		case 'p': game->race.tpads[2][4] = 0; break;
		case 27:
			tcStateRaceOut();
			game->state = TC_STATE_MAIN;
			tcStateMainIn();
		break;
		default: break;
	}
}

void
tcStateRaceSpecial(int key, int x, int y)
{
	TCGame *game;

	game = tcGame();

	switch(key)
	{
		case GLUT_KEY_UP: game->race.tpads[0][0] = 1; break;
		case GLUT_KEY_DOWN: game->race.tpads[0][1] = 1; break;
		case GLUT_KEY_LEFT: game->race.tpads[0][2] = 1; break;
		case GLUT_KEY_RIGHT: game->race.tpads[0][3] = 1; break;
		default: break;
	}
}

void
tcStateRaceSpecialUp(int key, int x, int y)
{
	TCGame *game;

	game = tcGame();

	switch(key)
	{
		case GLUT_KEY_UP: game->race.tpads[0][0] = 0; break;
		case GLUT_KEY_DOWN: game->race.tpads[0][1] = 0; break;
		case GLUT_KEY_LEFT: game->race.tpads[0][2] = 0; break;
		case GLUT_KEY_RIGHT: game->race.tpads[0][3] = 0; break;
		default: break;
	}
}

void
tcStateRaceJoystick(unsigned int buttonmask, int x, int y, int z)
{
	TCGame *game;

	game = tcGame();

	game->race.tpads[3][0] = y < -100;
	game->race.tpads[3][1] = y > 100;
	game->race.tpads[3][2] = x < -100;
	game->race.tpads[3][3] = x > 100;
	game->race.tpads[3][4] = (buttonmask & GLUT_JOYSTICK_BUTTON_A) != 0;

/*
	// tmp first player
	game->race.tpads[0][0] = y < -100;
	game->race.tpads[0][1] = y > 100;
	game->race.tpads[0][2] = x < -100;
	game->race.tpads[0][3] = x > 100;
	game->race.tpads[0][4] = (buttonmask & GLUT_JOYSTICK_BUTTON_A) != 0;
*/
}

static
void
tcStateRaceOnNear(void *data, dGeomID o1, dGeomID o2)
{
	TCGame *game;
	dBodyID b1, b2;
	dContact contacts[CONTACTS];
	dJointID c;
	int n;
	unsigned int i;

	game = data;

	b1 = dGeomGetBody(o1);
	b2 = dGeomGetBody(o2);

	if(b1 && b2 && dAreConnectedExcluding(b1, b2, dJointTypeContact))
		return;

	n = dCollide(o1, o2, CONTACTS, &contacts[0].geom, sizeof(dContact));

	if(n > 0)
	{
		for(i = 0; i < (unsigned int) n; i++)
		{
			contacts[i].surface.mode = dContactSlip1 | dContactSlip2 | dContactSoftERP | dContactSoftCFM | dContactApprox1 | dContactMu2;
			contacts[i].surface.mu = 0.75f; // 0.75
			contacts[i].surface.mu2 = 0.75f; // 0.75
			contacts[i].surface.slip1 = 0.01f; // 0.01
			contacts[i].surface.slip2 = 0.01f; // 0.01
			contacts[i].surface.soft_erp = 0.5f; // 0.5
			contacts[i].surface.soft_cfm = 0.5f; // 0.3
			c = dJointCreateContact(game->race.world, game->race.joints, &contacts[i]);
			dJointAttach(c, dGeomGetBody(contacts[i].geom.g1), dGeomGetBody(contacts[i].geom.g2));
		}
	}
}

static
void
tcStateRaceHuman(unsigned int player)
{
	TCGame *game;
	const float *p;
	float *t, d[3];

	game = tcGame();

	p = tcToyGetPosition(game->race.toys[player]);
	t = tcCheckPointsVertex(game->race.chps, (game->race.tchps[player] + 1) % tcCheckPointsVertices(game->race.chps));
	mlVectorSubTo(t, p, d);
	mlVectorNormTo(d, &game->race.tdistances[player]);
	if(game->race.tdistances[player] <= 30.0f)
	{
		game->race.tchps[player]++;
		t = tcCheckPointsVertex(game->race.chps, (game->race.tchps[player] + 1) % tcCheckPointsVertices(game->race.chps));
		mlVectorSubTo(t, p, d);
		mlVectorNormTo(d, &game->race.tdistances[player]);
	}


	if(game->race.titems[player] != -1)
	{
		switch(game->race.itypes[game->race.titems[player]])
		{
			case 1:
				if(game->race.tpads[player][4] && game->race.iacts[game->race.titems[player]] == 0)
				{
					alSource3f(game->race.tsfxs[1][player], AL_POSITION, p[0], p[1], p[2]);
					alSourcePlay(game->race.tsfxs[1][player]);
					game->race.iacts[game->race.titems[player]] = 180;
				}
			break;
			case 2:
				game->race.iacts[game->race.titems[player]] = game->race.tpads[player][4];
			break;
			case 3:
				if(game->race.tpads[player][4] && game->race.iacts[game->race.titems[player]] == 0)
				{
					alSource3f(game->race.tsfxs[2][player], AL_POSITION, p[0], p[1], p[2]);
					alSourcePlay(game->race.tsfxs[2][player]);
					game->race.iacts[game->race.titems[player]] = 180;
				}
			break;
			case 4:
				if(game->race.tpads[player][4] && game->race.iacts[game->race.titems[player]] == 0)
				{
					alSource3f(game->race.tsfxs[3][player], AL_POSITION, p[0], p[1], p[2]);
					alSourcePlay(game->race.tsfxs[3][player]);
					game->race.iacts[game->race.titems[player]] = 60 * 10;
				}
			break;
			default:
			break;
		}
	}

	tcToyDrive(game->race.toys[player], game->race.tpads[player][0], game->race.tpads[player][1], game->race.tpads[player][2],game->race.tpads[player][3]);
}

static
void
tcStateRaceComputer(unsigned int player)
{
	TCGame *game;
	const float *p, *r, *v;
	float *t, d[3], o[3], pln[4], ang, var, dst;
	int steer, gas, action;
	unsigned int i;

	game = tcGame();

	p = tcToyGetPosition(game->race.toys[player]);

	if(game->race.tgo[player])
		t = tcItemsVertex(game->race.items, game->race.tipick[player]);
	else
		t = tcCheckPointsVertex(game->race.chps, (game->race.tchps[player] + 1) % tcCheckPointsVertices(game->race.chps));

	mlVectorSubTo(t, p, d);
	mlVectorNormTo(d, &game->race.tdistances[player]);

	if(game->race.tgo[player] && game->race.tdistances[player] <= 10.0f || !game->race.tgo[player] && game->race.tdistances[player] <= 30.0f)
	{
		if(game->race.tgo[player])
		{
			game->race.tchps[player] += 4;
			game->race.tgo[player] = 0;
		}
		else
			game->race.tchps[player]++;

		if(game->race.titems[player] == -1)
		{
			for(i = 0; i < tcItemsVertices(game->race.items); i++)
			{
				if(game->race.tchps[player] % tcCheckPointsVertices(game->race.chps) == (game->race.ichps[i] + tcCheckPointsVertices(game->race.chps) - 2) % tcCheckPointsVertices(game->race.chps))
				{
					game->race.tgo[player] = (float) rand() / RAND_MAX <= 0.75;
					if(game->race.tgo[player])
						game->race.tipick[player] = i;
					break;
				}
			}
		}

		if(game->race.tgo[player])
			t = tcItemsVertex(game->race.items, game->race.tipick[player]);
		else
			t = tcCheckPointsVertex(game->race.chps, (game->race.tchps[player] + 1) % tcCheckPointsVertices(game->race.chps));
		mlVectorSubTo(t, p, d);
		mlVectorNormTo(d, &game->race.tdistances[player]);
	}

	mlVectorNormal(d);
	r = tcToyGetRotation(game->race.toys[player]);
	o[0] = r[2]; o[1] = r[6]; o[2] = r[10];
	mlVectorCrossProductTo(mlVectorY, o, pln);
	pln[3] = - mlVectorDotProduct(pln, p);
	dst = mlVectorDotProduct(pln, t) + pln[3];
	mlVectorAngleTo(o, d, &ang);
	var = (float) rand() / RAND_MAX;

	if(game->race.tgo[player])
	{
		if(dst > 5.0f * (0.25f + var * 0.75f))
			steer = 1;
		else if(dst < -5.0f * (0.25 + var * 0.75f))
			steer = -1;
		else
			steer = 0;
	}
	else
	{
		if(dst > 10.0f * (0.25f + var * 0.75f))
			steer = 1;
		else if(dst < -10.0f * (0.25 + var * 0.75f))
			steer = -1;
		else
			steer = 0;
	}

	if(ang > 75.0f)
	{
		v = tcToyGetLinearVel(game->race.toys[player]);
		if(game->race.tdistances[player] < 15.0f) // 40.0f
		{
			gas = (mlVectorNorm(v) > 2.0f) ? 0 : 1;
		}
		else
		{
			gas = -1;
			steer *= -1;
		}
	}
	else
		gas = (float) rand() / RAND_MAX >= (1.0f - game->options.race.difficulty);

	if(game->race.titems[player] != -1)
	{
		switch(game->race.itypes[game->race.titems[player]])
		{
			case 1:
				action = 0;
				o[0] = r[2]; o[1] = r[6]; o[2] = r[10];
				mlVectorScale(o, 5.0f);
				mlVectorAdd(o, p);
				for(i = 0; i < game->options.race.ntoys; i++)
				{
					if(i != player && tcToyIsEnabled(game->race.toys[i]))
					{
						const float *pi;

						pi = tcToyGetPosition(game->race.toys[i]);
						mlVectorSubTo(o, pi, d);
						if(mlVectorNorm(d) < 5.0f)
						{
							action = 1;
							break;
						}
					}
					
				}
				if(action && game->race.iacts[game->race.titems[player]] == 0)
				{
					alSource3f(game->race.tsfxs[1][player], AL_POSITION, p[0], p[1], p[2]);
					alSourcePlay(game->race.tsfxs[1][player]);
					game->race.iacts[game->race.titems[player]] = 180;
				}
			break;
			case 2:
				action = (float) rand() / RAND_MAX <= 0.5;
				game->race.iacts[game->race.titems[player]] = action;
			break;
			case 3:
				action = 0;
				o[0] = r[2]; o[1] = r[6]; o[2] = r[10];
				mlVectorScale(o, 30.0f);
				mlVectorAdd(o, p);
				for(i = 0; i < game->options.race.ntoys; i++)
				{
					if(i != player && tcToyIsEnabled(game->race.toys[i]))
					{
						const float *pi;

						pi = tcToyGetPosition(game->race.toys[i]);
						mlVectorSubTo(o, pi, d);
						if(mlVectorNorm(d) < 5.0f)
						{
							action = 1;
							break;
						}
					}
				}
				if(action && game->race.iacts[game->race.titems[player]] == 0)
				{
					alSource3f(game->race.tsfxs[2][player], AL_POSITION, p[0], p[1], p[2]);
					alSourcePlay(game->race.tsfxs[2][player]);
					game->race.iacts[game->race.titems[player]] = 180;
				}
			break;
			case 4:
				action = 0;
				o[0] = r[2]; o[1] = r[6]; o[2] = r[10];
				mlVectorScale(o, -10.0f);
				mlVectorAdd(o, p);
				for(i = 0; i < game->options.race.ntoys; i++)
				{
					if(i != player && tcToyIsEnabled(game->race.toys[i]))
					{
						const float *pi;

						pi = tcToyGetPosition(game->race.toys[i]);
						mlVectorSubTo(o, pi, d);
						if(mlVectorNorm(d) < 2.5f)
						{
							action = 1;
							break;
						}
					}
				}
				if(action && game->race.iacts[game->race.titems[player]] == 0)
				{
					alSource3f(game->race.tsfxs[3][player], AL_POSITION, p[0], p[1], p[2]);
					alSourcePlay(game->race.tsfxs[3][player]);
					game->race.iacts[game->race.titems[player]] = 60 * 10;
				}
			break;
			default:
			break;
		}
	}

	tcToyDrive(game->race.toys[player], gas == 1, gas == -1, steer == 1, steer == -1);
}

void
tcStateRaceFrame(void)
{
	TCGame *game;
	wchar_t buffer[128];
	const float *p, *r, *pj, *ip, *pb, *rb;
	float m[16], o[3], h[3], d[3], a[3], b[3];
	unsigned int first, actives, pactives, sw, sh, i, j;
	dMass mass;

	game = tcGame();

	for(i = 0; i < 8; i++)
	{
		dSpaceCollide(game->race.space, game, tcStateRaceOnNear);
		dWorldQuickStep(game->race.world, 0.02f);
		dJointGroupEmpty(game->race.joints);
	}

	first = 0;
	for(i = first; i < game->options.race.ntoys; i++)
	{
		if(!tcToyIsEnabled(game->race.toys[first]) || (tcToyIsEnabled(game->race.toys[i]) && (game->race.tchps[i] > game->race.tchps[first] || (game->race.tchps[i] == game->race.tchps[first] && game->race.tdistances[i] < game->race.tdistances[first]))))
			first = i;
	}

	p = tcToyGetPosition(game->race.toys[first]);

	mlVectorSubTo(p, game->race.camera, a);
	mlVectorScaleTo(a, 0.15f, b);
	mlVectorAdd(game->race.camera, b);

	/*
	p = tcToyGetPosition(game->race.toys[0]);
	r = tcToyGetRotation(game->race.toys[0]);
	o[0] = r[2]; o[1] = r[6]; o[2] = r[10];
	gluLookAt(p[0], p[1], p[2], p[0] + o[0], p[1] + o[1], p[2] + o[2], 0.0f, 1.0f, 0.0f);
	*/

	/*
	p = tcToyGetPosition(game->race.toys[first]);
	r = tcToyGetRotation(game->race.toys[first]);
	o[0] = r[2]; o[1] = 0.0f; o[2] = r[10];
	mlVectorScale(o, 12.5f);
	gluLookAt(game->race.camera[0] - o[0], game->race.camera[1] - o[1] + 0.5f, game->race.camera[2] - o[2], game->race.camera[0], game->race.camera[1], game->race.camera[2], 0.0f, 1.0f, 0.0f);
	alListener3f(AL_POSITION, game->race.camera[0] - o[0], game->race.camera[1] - o[1] + 62.5f, game->race.camera[2] - o[2]);
	*/

	//gluLookAt(game->race.camera[0], game->race.camera[1] + 62.5f, game->race.camera[2] + 62.5f, game->race.camera[0], game->race.camera[1], game->race.camera[2], 0.0f, 0.0f, -1.0f);
	gluLookAt(game->race.camera[0], game->race.camera[1] + 50.0f, game->race.camera[2] + 50.0f, game->race.camera[0], game->race.camera[1], game->race.camera[2], 0.0f, 0.0f, -1.0f);
	//gluLookAt(game->race.camera[0], game->race.camera[1] + 162.5f, game->race.camera[2], game->race.camera[0], game->race.camera[1], game->race.camera[2], 0.0f, 0.0f, -1.0f);
	//alListener3f(AL_POSITION, game->race.camera[0], game->race.camera[1] + 62.5f, game->race.camera[2] + 62.5f);
	alListener3f(AL_POSITION, game->race.camera[0], game->race.camera[1] + 50.0f, game->race.camera[2] + 50.0f);

//	glLightfv(GL_LIGHT0, GL_POSITION, position);

	mlFrustumCalc(game->race.frustum);

	tcTrackFrame(game->race.track);

	actives = 0;
	for(i = 0; i < game->options.race.ntoys; i++)
	{
		if(tcToyIsEnabled(game->race.toys[i]))
		{
			actives++;
			tcToyFrame(game->race.toys[i]);
		}
	}

	for(i = 0; i < tcItemsVertices(game->race.items); i++)
	{
		ip = tcItemsVertex(game->race.items, i);

		if(game->race.itoys[i] == -1)
		{
			for(j = 0; j < game->options.race.ntoys; j++)
			{
				if(tcToyIsEnabled(game->race.toys[j]))
				{
					pj = tcToyGetPosition(game->race.toys[j]);
					mlVectorSubTo(ip, pj, d);
					if(mlVectorNorm(d) < 5)
					{
						if(game->race.titems[j] != -1)
						{
							game->race.itoys[game->race.titems[j]] = -1;
							game->race.titems[j] = -1;
						}
						game->race.itoys[i] = j;
						game->race.itypes[i] = rand() % 4 + 1;
						switch(game->race.itypes[i])
						{
							case 1:
								game->race.iframes[i] = 60 * 10;
								game->race.iacts[i] = 0;
							break;
							case 2:
								game->race.iframes[i] = 60 * 10;
								game->race.iacts[i] = 0;
							break;
							case 3:
								game->race.iframes[i] = 60 * 10;
								game->race.iacts[i] = 0;
							break;
							case 4:
								game->race.iframes[i] = 60 * 10;
								game->race.iacts[i] = 0;
							break;
							default:
							break;
						}
						game->race.titems[game->race.itoys[i]] = i;

						alSource3f(game->race.tsfxs[0][game->race.itoys[i]], AL_POSITION, pj[0], pj[1], pj[2]);
						alSourcePlay(game->race.tsfxs[0][game->race.itoys[i]]);
					}
				}
			}
		}

		if(game->race.itoys[i] == -1)
		{
			glPushMatrix();
			glTranslatef(ip[0], ip[1], ip[2]);
			glRotatef((float) (game->general.display.frame * 4 % 360), 0.0f, 1.0f, 0.0f);
			mlMeshDraw(game->race.item);
			glPopMatrix();
		}
		else
		{
			p = tcToyGetPosition(game->race.toys[game->race.itoys[i]]);
			r = tcToyGetRotation(game->race.toys[game->race.itoys[i]]);

			if(game->race.itypes[i] == 1)
			{
				if(game->race.iframes[i] > 0)
				{
					if(tcToyIsEnabled(game->race.toys[game->race.itoys[i]]))
					{
						mlMatrixLoadPositionTransposedRotation(m, p, r);
						if(game->race.iacts[i] > 0)
						{
							if(game->race.iacts[i] > 90)
								mlMatrixRotate(m, mlVectorX, (float) (180 - game->race.iacts[i]));
							else if(game->race.iacts[i] < 90)
								mlMatrixRotate(m, mlVectorX, (float) (game->race.iacts[i]));
							else
							{
								mlMatrixRotate(m, mlVectorX, (float) (game->race.iacts[i]));
								o[0] = r[2]; o[1] = r[6]; o[2] = r[10];
								mlVectorScale(o, 5.0f);
								mlVectorAddTo(p, o, h);

								for(j = 0; j < game->options.race.ntoys; j++)
								{
									if(game->race.itoys[i] != j && tcToyIsEnabled(game->race.toys[j]))
									{
										pj = tcToyGetPosition(game->race.toys[j]);
										mlVectorSubTo(pj, h, d);

										if(mlVectorNorm(d) <= 5.0f)
										{
											alSource3f(game->race.tsfxs[5][j], AL_POSITION, pj[0], pj[1], pj[2]);
											alSourcePlay(game->race.tsfxs[5][j]);

											tcToyDisable(game->race.toys[j]);

											if(actives == game->options.race.ntoys && game->race.tpoints[j])
												game->race.tpoints[j]--;

											actives--;
										}
									}
								}

								alSource3f(game->race.tsfxs[4][game->race.itoys[i]], AL_POSITION, p[0], p[1], p[2]);
								alSourcePlay(game->race.tsfxs[4][game->race.itoys[i]]);
							}

							game->race.iacts[i] -= 10;
						}

						glPushMatrix();
						glMultMatrixf(m);
						mlMeshDraw(game->race.hammer);
						glPopMatrix();
					}

					game->race.iframes[i]--;
				}
				else
				{
					alSource3f(game->race.tsfxs[5][game->race.itoys[i]], AL_POSITION, p[0], p[1], p[2]);
					alSourcePlay(game->race.tsfxs[5][game->race.itoys[i]]);

					game->race.titems[game->race.itoys[i]] = -1;
					game->race.itoys[i] = -1;
				}
			}
			else if(game->race.itypes[i] == 2)
			{
				if(game->race.iframes[i] > 0)
				{
					if(tcToyIsEnabled(game->race.toys[game->race.itoys[i]]))
					{
						if(game->race.iacts[i] > 0)
							tcToyTurbo(game->race.toys[game->race.itoys[i]]);
						else
							tcToyUnturbo(game->race.toys[game->race.itoys[i]]);
					}
					
					game->race.iframes[i]--;
				}
				else
				{
					tcToyUnturbo(game->race.toys[game->race.itoys[i]]);

					alSource3f(game->race.tsfxs[5][game->race.itoys[i]], AL_POSITION, p[0], p[1], p[2]);
					alSourcePlay(game->race.tsfxs[5][game->race.itoys[i]]);

					game->race.titems[game->race.itoys[i]] = -1;
					game->race.itoys[i] = -1;
				}
				
			}
			else if(game->race.itypes[i] == 3)
			{
				if(game->race.iframes[i] > 0)
				{
					if(tcToyIsEnabled(game->race.toys[game->race.itoys[i]]))
					{
						if(game->race.iacts[i] == 180)
						{
							game->race.ibodies[i] = dBodyCreate(game->race.world);
							dMassSetSphere(&mass, 0.05f, 1.0f);
							dMassAdjust(&mass, 0.05f);
							dBodySetMass(game->race.ibodies[i], &mass);
							game->race.igeoms[i] = dCreateSphere(game->race.space, 1.0f);
							dGeomSetBody(game->race.igeoms[i], game->race.ibodies[i]);

							dBodySetPosition(game->race.ibodies[i], p[0], p[1] + 3.5f, p[2]);
							o[0] = r[2]; o[1] = r[6]; o[2] = r[10];
							mlVectorScale(o, 50.0f);

							pj = tcToyGetLinearVel(game->race.toys[game->race.itoys[i]]);

							dBodyAddForce(game->race.ibodies[i], o[0] + pj[0], o[1] + 25.0f + pj[1], o[2] + pj[2]);

							game->race.iacts[i] -= 10;
						}
						else if(game->race.iacts[i] > 0)
						{
							pb = dBodyGetPosition(game->race.ibodies[i]);

							for(j = 0; j < game->options.race.ntoys; j++)
							{
								if(game->race.itoys[i] != j && tcToyIsEnabled(game->race.toys[j]))
								{
									pj = tcToyGetPosition(game->race.toys[j]);
									mlVectorSubTo(pj, pb, d);

									if(mlVectorNorm(d) <= 7.5f)
									{
										alSource3f(game->race.tsfxs[5][j], AL_POSITION, pj[0], pj[1], pj[2]);
										alSourcePlay(game->race.tsfxs[5][j]);

										tcToyDisable(game->race.toys[j]);

										if(actives == game->options.race.ntoys && game->race.tpoints[j])
											game->race.tpoints[j]--;

										actives--;
									}
								}
							}

							game->race.iacts[i] -= 10;
						}
						else
						{
							if(game->race.ibodies[i])
							{
								pb = dBodyGetPosition(game->race.ibodies[i]);

								alSource3f(game->race.tsfxs[4][game->race.itoys[i]], AL_POSITION, pb[0], pb[1], pb[2]);
								alSourcePlay(game->race.tsfxs[4][game->race.itoys[i]]);

								dGeomDestroy(game->race.igeoms[i]);
								dBodyDestroy(game->race.ibodies[i]);
								game->race.ibodies[i] = NULL;
							}
						}

						mlMatrixLoadPositionTransposedRotation(m, p, r);

						glPushMatrix();
						glMultMatrixf(m);
						mlMeshDraw(game->race.canon);
						glPopMatrix();

						if(game->race.ibodies[i])
						{
							pb = dBodyGetPosition(game->race.ibodies[i]);
							rb = dBodyGetRotation(game->race.ibodies[i]);
							mlMatrixLoadPositionTransposedRotation(m, pb, rb);

							glPushMatrix();
							glMultMatrixf(m);
							mlMeshDraw(game->race.ball);
							glPopMatrix();
						}
					}

					game->race.iframes[i]--;
				}
				else
				{
					alSource3f(game->race.tsfxs[5][game->race.itoys[i]], AL_POSITION, p[0], p[1], p[2]);
					alSourcePlay(game->race.tsfxs[5][game->race.itoys[i]]);

					game->race.titems[game->race.itoys[i]] = -1;
					game->race.itoys[i] = -1;
				}
			}
			else if(game->race.itypes[i] == 4)
			{
				if(game->race.iframes[i] > 0)
				{
					if(tcToyIsEnabled(game->race.toys[game->race.itoys[i]]))
					{
						if(game->race.iacts[i] == 60 * 10)
						{
							game->race.ibodies[i] = dBodyCreate(game->race.world);
							dMassSetBox(&mass, 0.5f, 2.5f, 0.5f, 2.5f); /// 1.0f, 2.5f, 1.0f, 5.5f
							dMassAdjust(&mass, 0.05f);
							dBodySetMass(game->race.ibodies[i], &mass);

							game->race.igeoms[i] = dCreateBox(game->race.space, 2.5f, 0.5f, 2.5f);
							dGeomSetBody(game->race.igeoms[i], game->race.ibodies[i]);

							o[0] = r[2]; o[1] = r[6]; o[2] = r[10];
							mlVectorScale(o, -5.0f);
							mlVectorAdd(o, p);

							dBodySetPosition(game->race.ibodies[i], o[0], o[1] + 5.0f, o[2]);

							game->race.iacts[i] -= 1;
						}
						else if(game->race.iacts[i] > 0)
						{
							pb = dBodyGetPosition(game->race.ibodies[i]);

							for(j = 0; j < game->options.race.ntoys; j++)
							{
								if(game->race.itoys[i] != j && tcToyIsEnabled(game->race.toys[j]))
								{
									pj = tcToyGetPosition(game->race.toys[j]);
									mlVectorSubTo(pj, pb, d);

									if(mlVectorNorm(d) <= 5.0f)
									{
										alSource3f(game->race.tsfxs[5][j], AL_POSITION, pb[0], pb[1], pb[2]);
										alSourcePlay(game->race.tsfxs[5][j]);

										tcToyDisable(game->race.toys[j]);

										if(actives == game->options.race.ntoys && game->race.tpoints[j])
											game->race.tpoints[j]--;

										actives--;
									}
								}
							}

							game->race.iacts[i] -= 1;
						}
						else
						{
							if(game->race.ibodies[i])
							{
								pb = dBodyGetPosition(game->race.ibodies[i]);

								alSource3f(game->race.tsfxs[4][game->race.itoys[i]], AL_POSITION, pb[0], pb[1], pb[2]);
								alSourcePlay(game->race.tsfxs[4][game->race.itoys[i]]);

								dGeomDestroy(game->race.igeoms[i]);
								dBodyDestroy(game->race.ibodies[i]);
								game->race.ibodies[i] = NULL;
							}
						}

						if(game->race.ibodies[i])
						{
							pb = dBodyGetPosition(game->race.ibodies[i]);
							rb = dBodyGetRotation(game->race.ibodies[i]);
							mlMatrixLoadPositionTransposedRotation(m, pb, rb);

							glPushMatrix();
							glMultMatrixf(m);
							mlMeshDraw(game->race.mine);
							glPopMatrix();
						}
						else
						{
							o[0] = r[2]; o[1] = r[6]; o[2] = r[10];
							mlVectorScale(o, -5.0f);
							mlVectorAdd(o, p);
							o[1] += 5.0f;
							mlMatrixLoadPositionTransposedRotation(m, o, r);

							glPushMatrix();
							glMultMatrixf(m);
							mlMeshDraw(game->race.mine);
							glPopMatrix();
						}
					}

					game->race.iframes[i]--;
				}
				else
				{
					alSource3f(game->race.tsfxs[5][game->race.itoys[i]], AL_POSITION, p[0], p[1], p[2]);
					alSourcePlay(game->race.tsfxs[5][game->race.itoys[i]]);

					game->race.titems[game->race.itoys[i]] = -1;
					game->race.itoys[i] = -1;
				}
			}
		}
	}

	first = 0;
	for(i = first; i < game->options.race.ntoys; i++)
	{
		if(!tcToyIsEnabled(game->race.toys[first]) || (tcToyIsEnabled(game->race.toys[i]) && (game->race.tchps[i] > game->race.tchps[first] || (game->race.tchps[i] == game->race.tchps[first] && game->race.tdistances[i] < game->race.tdistances[first]))))
			first = i;
	}

	switch(game->race.state)
	{
		case TC_RACE_STATE_PREPARE_POINT:
			if(game->race.start == 0)
			{
				tcToysPrepare(game->race.tchps[first]);

				game->race.state = TC_RACE_STATE_FIGHT_POINT;
				game->race.start = 60;
			}

		break;
		case TC_RACE_STATE_FIGHT_POINT:
			if(game->race.start == 0)
			{
				for(i = 0; i < game->options.race.nplayers; i++)
				{
					if(tcToyIsEnabled(game->race.toys[i]))
						tcStateRaceHuman(game->race.toys[i]); 
				}

				for(i = game->options.race.nplayers; i < game->options.race.ntoys; i++)
				{
					if(tcToyIsEnabled(game->race.toys[i]))
						tcStateRaceComputer(game->race.toys[i]); 
				}

				pactives = 0;
				for(i = 0; i < game->options.race.nplayers; i++)
					if(tcToyIsEnabled(game->race.toys[i]))
						pactives++;
				if(pactives == 0)
				{
					for(i = game->options.race.nplayers; i < game->options.race.ntoys; i++)
					{
						if(i != first && tcToyIsEnabled(game->race.toys[i]))
						{
							p = tcToyGetPosition(game->race.toys[i]);

							alSource3f(game->race.tsfxs[5][i], AL_POSITION, p[0], p[1], p[2]);
							alSourcePlay(game->race.tsfxs[5][i]);

							tcToyDisable(game->race.toys[i]);
							actives--;
						}
					}
				}

				if(actives > 1)
				{
					for(i = 0; i < game->options.race.ntoys; i++)
					{
						if(tcToyIsEnabled(game->race.toys[i]) && !mlFrustumPoint(game->race.frustum, tcToyGetPosition(game->race.toys[i])))
						{
							p = tcToyGetPosition(game->race.toys[i]);

							alSource3f(game->race.tsfxs[5][i], AL_POSITION, p[0], p[1], p[2]);
							alSourcePlay(game->race.tsfxs[5][i]);

							tcToyDisable(game->race.toys[i]);

							if(actives == game->options.race.ntoys && game->race.tpoints[i])
								game->race.tpoints[i]--;

							actives--;
						}
					}
				}
				else
				{
					tcToyDrive(game->race.toys[first], 0, 0, 0, 0);
					tcToyJump(game->race.toys[first]);

					game->race.state = TC_RACE_STATE_WIN_POINT;
					game->race.start = 60;
				}
			}
			else
			{
				swprintf(buffer, 128, L"%ls", L"Ready!");

				tlStringLoad(game->race.string, buffer);
				flFontStringMetrics(game->general.font, game->race.string, &sw, &sh);

				tcGame2D();

				mlMaterialBind(game->general.fontmat);

				glDisable(GL_CULL_FACE);
				glEnable(GL_TEXTURE_GEN_S);
				glEnable(GL_TEXTURE_GEN_T);
				glMatrixMode(GL_TEXTURE);
				glPushMatrix();
				glScalef(10.0f, 10.0f, 10.0f);
				glMatrixMode(GL_MODELVIEW);

				glPushMatrix();
				glScalef(2.0f * game->general.display.aspect, 2.0f * game->general.display.aspect, 2.0f * game->general.display.aspect);
				glTranslatef(- (float) sw / 2.0f, - (float) sh / 2.0f, 0.0f);
				flFontDrawStringFill(game->general.font, game->race.string);
				glPopMatrix();

				glMatrixMode(GL_TEXTURE);
				glPopMatrix();
				glMatrixMode(GL_MODELVIEW);
				glDisable(GL_TEXTURE_GEN_S);
				glDisable(GL_TEXTURE_GEN_T);
				glEnable(GL_CULL_FACE);

				tcGame3D();

				tlStringUnload(game->race.string);
			}

		break;
		case TC_RACE_STATE_WIN_POINT:

			if(game->race.start == 0)
			{
				game->race.tpoints[first]++;
				if(game->race.tpoints[first] < game->options.race.npoints)
				{
					game->race.state = TC_RACE_STATE_PREPARE_POINT;
					game->race.start = 0;
				}
				else
				{
					game->race.state = TC_RACE_STATE_WIN;
					game->race.start = 60;
				}
			}
			else
			{
				swprintf(buffer, 128, L"%ls Toy Wins Point", tlStringData(game->general.tcolors[first]));

				tlStringLoad(game->race.string, buffer);
				flFontStringMetrics(game->general.font, game->race.string, &sw, &sh);

				tcGame2D();

				mlMaterialBind(game->general.fontmat);

				glDisable(GL_CULL_FACE);
				glEnable(GL_TEXTURE_GEN_S);
				glEnable(GL_TEXTURE_GEN_T);
				glMatrixMode(GL_TEXTURE);
				glPushMatrix();
				glScalef(10.0f, 10.0f, 10.0f);
				glMatrixMode(GL_MODELVIEW);

				glPushMatrix();
				glScalef(2.0f * game->general.display.aspect, 2.0f * game->general.display.aspect, 2.0f * game->general.display.aspect);
				glTranslatef(- (float) sw / 2.0f, - (float) sh / 2.0f, 0.0f);
				flFontDrawStringFill(game->general.font, game->race.string);
				glPopMatrix();

				glMatrixMode(GL_TEXTURE);
				glPopMatrix();
				glMatrixMode(GL_MODELVIEW);
				glDisable(GL_TEXTURE_GEN_S);
				glDisable(GL_TEXTURE_GEN_T);
				glEnable(GL_CULL_FACE);

				tcGame3D();

				tlStringUnload(game->race.string);
			}

		break;
		case TC_RACE_STATE_WIN:
			if(game->race.start == 0)
			{
				p = tcToyGetPosition(game->race.toys[first]);

				alSource3f(game->race.tsfxs[6][first], AL_POSITION, p[0], p[1], p[2]);
				alSourcePlay(game->race.tsfxs[6][first]);

				tcToyJump(game->race.toys[first]);

				game->race.state = TC_RACE_STATE_OUT;
				game->race.start = 120;
			}
			else
			{
				swprintf(buffer, 128, L"%ls Toy Wins Race", tlStringData(game->general.tcolors[first]));

				tlStringLoad(game->race.string, buffer);
				flFontStringMetrics(game->general.font, game->race.string, &sw, &sh);

				tcGame2D();

				mlMaterialBind(game->general.fontmat);

				glDisable(GL_CULL_FACE);
				glEnable(GL_TEXTURE_GEN_S);
				glEnable(GL_TEXTURE_GEN_T);
				glMatrixMode(GL_TEXTURE);
				glPushMatrix();
				glScalef(10.0f, 10.0f, 10.0f);
				glMatrixMode(GL_MODELVIEW);

				glPushMatrix();
				glScalef(2.0f * game->general.display.aspect, 2.0f * game->general.display.aspect, 2.0f * game->general.display.aspect);
				glTranslatef(- (float) sw / 2.0f, - (float) sh / 2.0f, 0.0f);
				flFontDrawStringFill(game->general.font, game->race.string);
				glPopMatrix();

				glMatrixMode(GL_TEXTURE);
				glPopMatrix();
				glMatrixMode(GL_MODELVIEW);
				glDisable(GL_TEXTURE_GEN_S);
				glDisable(GL_TEXTURE_GEN_T);
				glEnable(GL_CULL_FACE);

				tcGame3D();

				tlStringUnload(game->race.string);
			}

		break;
		case TC_RACE_STATE_OUT:
			if(game->race.start == 0)
			{
				tcStateRaceOut();
				game->state = TC_STATE_MAIN;
				tcStateMainIn();
			}
		break;
	}

	if(game->race.start > 0)
		game->race.start--;
}
